import time as lib_time

from django.utils.translation import gettext_lazy, gettext as _
from rest_framework.permissions import IsAuthenticated
from rest_framework.serializers import Serializer
from rest_framework.response import Response
from rest_framework.decorators import action
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi

from core import errors
from utils.paginators import NoPaginatorInspector
from apps.api.paginations import NewPageNumberPagination100
from apps.api.viewsets import NormalGenericViewSet
from apps.app_tx_meeting.models import MeetingRoomType
from apps.app_tx_meeting.helpers import MeetReservationHelper,RoomReserveTimeIntervalCounter, TimeLine
from apps.app_tx_meeting.managers import MeetingUserWrapper


class MeetingRoomIntervalsViewSet(NormalGenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = NewPageNumberPagination100
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('会议室资源已预定区间统计'),
        manual_parameters=[
            openapi.Parameter(
                name='start_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询开始时间戳')
            ),
            openapi.Parameter(
                name='end_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询结束时间戳')
            ),
            openapi.Parameter(
                name='room_type',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description=gettext_lazy('查询指定会议室类型。[50, 100, 300, 500, 1000]')
            )
        ],
        paginator_inspectors=[NoPaginatorInspector],
        responses={
            200: ''''''
        }
    )
    def list(self, request, *args, **kwargs):
        """
        会议室资源已预定区间统计

            * 每30分钟一个区间统计各规格会议室总数和已预定数
            * 查询的时间区间 start_time - end_time 不能大于24小时；开始和结束时间不是30分钟或整时对齐的，返回的数据时间区间会外扩30分钟或整时对齐

            http code 200:
            {
                "start_time": 1745370000,
                "end_time": 1745371800,
                "data": [
                    {
                        "room_type": 50,     # 会议室规格类型
                        "intervals": [
                            {
                                "start": 1745370000,
                                "end": 1745371800,
                                "reserved": 6,  # 已预定会议室数
                                "total": 50     # 会议室总数
                            }
                        ]
                    }
                ]
            }
        """
        start_time = request.query_params.get('start_time')
        end_time = request.query_params.get('end_time')
        room_type = request.query_params.get('room_type')

        if not start_time or not end_time:
            return self.exception_response(errors.InvalidArgument(message=_('必须同时指定查询开始时间和结束时间')))

        try:
            start_time = int(start_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询开始时间无效')))

        try:
            end_time = int(end_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询结束时间无效')))

        if not ((60 * 15) <= (end_time - start_time) <= (3600 * 24)):
            return self.exception_response(
                errors.InvalidArgument(message=_('查询时间区间无效，时间区间范围大小必须在15分钟至24小时之间')))

        if room_type:
            try:
                room_type = int(room_type)
                if room_type not in MeetingRoomType.values:
                    raise ValueError('会议室类型w无效')
            except ValueError:
                return self.exception_response(errors.InvalidArgument(message=_('会议室类型无效')))
        else:
            room_type = None

        reserved_internals = RoomReserveTimeIntervalCounter().get_room_reserved_internals(
            time_interval=TimeLine(start=start_time, end=end_time), room_type=room_type
        )
        return Response(data={
            'start_time': start_time,
            'end_time': end_time,
            'data': reserved_internals,
        })

    def get_serializer_class(self):
        return Serializer


class MeetingRoomUsableViewSet(NormalGenericViewSet):
    permission_classes = [IsAuthenticated]
    pagination_class = NewPageNumberPagination100
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('查询一个时间段内会议室资源可预定数'),
        manual_parameters=[
            openapi.Parameter(
                name='start_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询开始时间戳')
            ),
            openapi.Parameter(
                name='end_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询结束时间戳')
            ),
            openapi.Parameter(
                name='room_type',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description=gettext_lazy('查询指定会议室类型。[50, 100, 300, 500, 1000]')
            )
        ],
        paginator_inspectors=[NoPaginatorInspector],
        responses={
            200: ''''''
        }
    )
    def list(self, request, *args, **kwargs):
        """
        查询一个时间段内会议室资源已预定数、可用数信息

            * 用户查询时间区间（用户预定时间段）不能超过24小时
            * 由于一个会议预定实际占用的会议室资源时间段 = 会议预定时间段 + 前后默认多预留时间
              数据统计实际的“统计时间段”是包含会议前后默认多预留的时间，在此“统计时间段”内有可用会议室资源时才能满足“用户预定时间段”
            * 管理员可看到普通用户不可见的会议室类型

            http code 200:
            {
                "start_time": 1745370000,   # 用户查询开始时间
                "end_time": 1745371800,     # 用户查询结束时间
                "count_start": 1745368000,  # 实际统计开始时间
                "count_end": 1745372000,    # 实际统计结束时间
                "data": [                   # 可能为空数组，各规格类型的会议室未开启可见时不返回对应会议室信息
                    {
                        "room_type": 50,     # 会议室规格类型
                        "reserved": 6,  # 已预定会议室数
                        "total": 50     # 会议室总数
                    }
                ]
            }
        """
        start_time = request.query_params.get('start_time')
        end_time = request.query_params.get('end_time')
        room_type = request.query_params.get('room_type')

        if not start_time or not end_time:
            return self.exception_response(errors.InvalidArgument(message=_('必须同时指定查询开始时间和结束时间')))

        try:
            start_time = int(start_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询开始时间无效')))

        try:
            end_time = int(end_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询结束时间无效')))

        if not ((60 * 15) <= (end_time - start_time) <= (3600 * 24)):
            return self.exception_response(
                errors.InvalidArgument(message=_('查询时间区间无效，时间区间范围大小必须在15分钟至24小时之间')))

        if room_type:
            try:
                room_type = int(room_type)
                if room_type not in MeetingRoomType.values:
                    raise ValueError('会议室类型w无效')
            except ValueError:
                return self.exception_response(errors.InvalidArgument(message=_('会议室类型无效')))
        else:
            room_type = None

        meet_user = MeetingUserWrapper(user=request.user)
        # 加上会议预定时间段前后多预留的时间
        room_reserve_start, room_reserve_end = MeetReservationHelper.room_reserve_start_end(
            start_time=start_time, end_time=end_time)
        usable_data = RoomReserveTimeIntervalCounter.meeting_room_usable(
            query_interval=TimeLine(start=room_reserve_start, end=room_reserve_end), room_type=room_type,
            is_admin=meet_user.is_admin()
        )
        return Response(data={
            'start_time': start_time,
            'end_time': end_time,
            'count_start': room_reserve_start,
            'count_end': room_reserve_end,
            'data': usable_data,
        })

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('会议室资源可预定数区间统计'),
        manual_parameters=[
            openapi.Parameter(
                name='start_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询开始时间戳')
            ),
            openapi.Parameter(
                name='end_time',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=True,
                description=gettext_lazy('查询结束时间戳')
            ),
            openapi.Parameter(
                name='room_type',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description=gettext_lazy('查询指定会议室类型。[50, 100, 300, 500, 1000]')
            )
        ],
        paginator_inspectors=[NoPaginatorInspector],
        responses={
            200: ''''''
        }
    )
    @action(methods=['GET'], detail=False, url_path='intervals', url_name='intervals')
    def usable_intervals(self, request, *args, **kwargs):
        """
        会议室资源可预定数区间统计

            * 每30分钟一个区间统计各规格会议室总数和可预定数
            * 查询的时间区间 start_time - end_time 不能大于24小时；开始和结束时间不是30分钟或整时对齐的，返回的数据时间区间会外扩30分钟或整时对齐
            * 查询“开始时间”不能小于当前时间，不能大于7日后的时间；“结束时间”超过7日后的时间 替换为7日后的时间；只能预定7日内，7日后相当于无可预定会议室资源

            http code 200:
            {
                "start_time": 1745370000,
                "end_time": 1745371800,
                "data": [               # 可能为空数组，各规格类型的会议室未开启可见时不返回对应会议室信息
                    {
                        "room_type": 50,     # 会议室规格类型
                        "intervals": [
                            {
                                "start": 1745370000,
                                "end": 1745371800,
                                "reservable": 6,  # 区间可预定会议室数
                                "total": 50     # 会议室总数
                            }
                        ]
                    }
                ]
            }
        """
        start_time = request.query_params.get('start_time')
        end_time = request.query_params.get('end_time')
        room_type = request.query_params.get('room_type')

        if not start_time or not end_time:
            return self.exception_response(errors.InvalidArgument(message=_('必须同时指定查询开始时间和结束时间')))

        try:
            start_time = int(start_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询开始时间无效')))

        try:
            end_time = int(end_time)
        except ValueError:
            return self.exception_response(errors.InvalidArgument(message=_('查询结束时间无效')))

        if not ((60 * 15) <= (end_time - start_time) <= (3600 * 24)):
            return self.exception_response(
                errors.InvalidArgument(message=_('查询时间区间无效，时间区间范围大小必须在15分钟至24小时之间')))

        now_ts = int(lib_time.time())
        after_7days_ts = now_ts + 3600 * 24 * 7
        if start_time <= now_ts:
            return self.exception_response(errors.InvalidArgument(message=_('查询开始时间不能小于当前时间')))

        if start_time >= after_7days_ts:
            return self.exception_response(errors.InvalidArgument(message=_('查询开始时间不能超过7日内时间')))

        if end_time > after_7days_ts:
            end_time = after_7days_ts

        if room_type:
            try:
                room_type = int(room_type)
                if room_type not in MeetingRoomType.values:
                    raise ValueError('会议室类型无效')
            except ValueError:
                return self.exception_response(errors.InvalidArgument(message=_('会议室类型无效')))
        else:
            room_type = None

        reserved_internals = RoomReserveTimeIntervalCounter().meeting_room_usable_internals(
            time_interval=TimeLine(start=start_time, end=end_time), room_type=room_type
        )
        return Response(data={
            'start_time': start_time,
            'end_time': end_time,
            'data': reserved_internals,
        })

    def get_serializer_class(self):
        return Serializer
